package com.netsdk.utils;

import com.netsdk.common.SavePath;
import com.netsdk.demo.module.LoginModule;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.ToolKits;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

/**
 * 下面是封装好的工具类，可直接食用。
 *
 * @Author: ganbo
 * @Date: 2021/6/16 18:14
 */
public class CapturePictureUtil {

    private static NetSDKLib netsdk = NetSDKLib.NETSDK_INSTANCE;

    private static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();

    private static NetSDKLib.LLong m_hLoginHandle = new NetSDKLib.LLong(0);

    private static DisConnect disConnect = new DisConnect();

    private static HaveReConnect haveReConnect = new HaveReConnect();

    private static fCaptureReceiveCB m_CaptureReceiveCB = new fCaptureReceiveCB();

    public static void main(String[] args) {
        capturePicture("192.168.1.108", 37777, "admin", "dahua123456,.", 1, 0, 0);
    }

    /**
     * 封装抓图方法
     *
     * @param m_strIp       设备ip
     * @param m_nPort       设备端口
     * @param m_strUser     账号
     * @param m_strPassword 密码
     * @param chn           通道id
     * @param mode          请求一帧  默认 0
     * @param interval      时间单位秒：默认 0
     */
    public static void capturePicture(String m_strIp, int m_nPort, String m_strUser, String m_strPassword, int chn, int mode, int interval) {

        // 初始化sdk
        boolean init = LoginModule.init(disConnect, haveReConnect);
        if (init) {
            System.out.println("初始化成功");
        }

        // 登录
        if (m_hLoginHandle.longValue() == 0) {
            login(m_strIp, m_nPort, m_strUser, m_strPassword);
        }
        // 截图
        if (m_hLoginHandle.longValue() != 0) {
            snapPicture(chn, mode, interval);
        }
        try {
            synchronized (fCaptureReceiveCB.class) {
                // 默认等待 3s, 防止设备断线时抓拍回调没有被触发，而导致死等
                fCaptureReceiveCB.class.wait(3000L);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("--> " + Thread.currentThread().getName() + " CLIENT_SnapPictureEx Success." + System.currentTimeMillis());
        logout();
        LoginModule.cleanup();
    }


    /*
     * 登录
     */
    public static boolean login(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {

        //入参
        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
        pstInParam.nPort = m_nPort;
        pstInParam.szIP = m_strIp.getBytes();
        pstInParam.szPassword = m_strPassword.getBytes();
        pstInParam.szUserName = m_strUser.getBytes();
        //出参
        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
        pstOutParam.stuDeviceInfo = m_stDeviceInfo;
        m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
        System.out.println(netsdk.getClass());
        if (m_hLoginHandle.longValue() == 0) {
            System.err.printf("登录失败！\n", m_strIp, m_nPort, ToolKits.getErrorCodePrint());
        } else {
            System.out.println("登录成功： [ " + m_strIp + " ]");

        }

        return m_hLoginHandle.longValue() == 0 ? false : true;
    }


    /**
     * 退出登录
     */
    private static boolean logout() {
        if (m_hLoginHandle.longValue() == 0) {
            return false;
        }

        boolean bRet = netsdk.CLIENT_Logout(m_hLoginHandle);
        if (bRet) {
            m_hLoginHandle.setValue(0);
        }

        return bRet;
    }

    /**
     * 抓图方法
     *
     * @param chn      通道号
     * @param mode     默认 0
     * @param interval 默认0
     * @return 成功返回ture
     */
    public static boolean snapPicture(int chn, int mode, int interval) {
        setSnapRevCallBack(m_CaptureReceiveCB);
        // send caputre picture command to device
        NetSDKLib.SNAP_PARAMS stuSnapParams = new NetSDKLib.SNAP_PARAMS();
        stuSnapParams.Channel = chn;            // channel
        stuSnapParams.mode = mode;                // capture picture mode
        stuSnapParams.Quality = 3;                // picture quality
        stuSnapParams.InterSnap = interval;    // timer capture picture time interval
        stuSnapParams.CmdSerial = 0;            // request serial

        IntByReference reserved = new IntByReference(0);
        if (!netsdk.CLIENT_SnapPictureEx(m_hLoginHandle, stuSnapParams, reserved)) {
            System.err.printf("CLIENT_SnapPictureEx Failed!" + ToolKits.getErrorCodePrint());
            return false;
        } else {
            System.out.println("CLIENT_SnapPictureEx success");
        }
        return true;
    }

    /**
     * 保存图片
     */
    private static class fCaptureReceiveCB implements NetSDKLib.fSnapRev {
        BufferedImage bufferedImage = null;

        public void invoke(NetSDKLib.LLong lLoginID, Pointer pBuf, int RevLen, int EncodeType, int CmdSerial, Pointer dwUser) {
            if (pBuf != null && RevLen > 0) {
                String strFileName = SavePath.getSavePath().getSaveCapturePath();
                System.out.println("strFileName = " + strFileName);
                byte[] buf = pBuf.getByteArray(0, RevLen);
                ByteArrayInputStream byteArrInput = new ByteArrayInputStream(buf);
                try {
                    bufferedImage = ImageIO.read(byteArrInput);
                    if (bufferedImage == null) {
                        return;
                    }
                    ImageIO.write(bufferedImage, "jpg", new File(strFileName));
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * 抓图回调函数
     *
     * @param cbSnapReceive
     */
    private static void setSnapRevCallBack(NetSDKLib.fSnapRev cbSnapReceive) {
        netsdk.CLIENT_SetSnapRevCallBack(cbSnapReceive, null);
    }


    // 设备断线回调: 通过 CLIENT_Init 设置该回调函数，当设备出现断线时，SDK会调用该函数
    private static class DisConnect implements NetSDKLib.fDisConnect {
        public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
            System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
        }
    }

    // 网络连接恢复，设备重连成功回调
    // 通过 CLIENT_SetAutoReconnect 设置该回调函数，当已断线的设备重连成功时，SDK会调用该函数
    private static class HaveReConnect implements NetSDKLib.fHaveReConnect {
        public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
            System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);
        }
    }
}

